var widget = widget || { };
/**
 * Это класс виджета вкладок.
 * @lends widget.Tabs
 */
widget.Tabs = Base.extend({
	
	id : undefined,			// идентификатор объекта
	
	type : 'tabs',			// тип объекта

	tabs : [],				// масив элементов контейнера

	visible : true,			// признак того, что контейнер будет отображаться
	
	WIDGET_CLASS : 'wg_tabs',

	/**
	 * Конструктор класса
	 * @augments Base
	 * @class Класс виджета Вкладки.
	 *
	 * <p><b>CSS класс для DOM элемента - <code>wg_tabs</code></b></p>
	 * 
	 * <p><b>Параметры объекта конфигурации:</b></p>
	 * {String} <b>id</b> - Идентификатор компонента.</br>
	 * {String} <b>cls</b> - Имя класса css (или нескольких классов через пробел)
	 * которые будут добавлены контейнеру.</br>
	 * {Int} <b>width</b> - Ширина компонента.</br>
	 * {Int} <b>height</b> - Высота компонента.</br>
	 * {Object[]} <b>items</b> - Массив компонент, которые должны быть включены в контейнер.</br>
	 * {boolean} <b>scrolling</b> - Признак того, что элементы внутри контейнера могут 
	 * скролироваться, если сумма высот всех входящих элементов больше высоты контейнера.</br>
	 * {ElementDOM} <b>renderTo</b> - Элемент DOM, в который будет прорисован контейнер.</br>
	 * {Object[]} <b>listeners</b> - Массив слушателей событий.</br>
	 * 
	 * <p><b>События:</b></p>
	 * <b>afterrender</b> - Вызывается сразу после прорисовки компонента.</br>
	 * <b>click</b> - Вызывается при клике на область компонента.</br>
	 * <b>itemClick</b> - Вызывается при клике на область любого элемента компонента.</br>
	 * <b>hide</b> - Вызывается когда компонент был спрятан.</br>
	 * <b>show</b> - Вызывается когда компонент был показан.</br>
	 * 
	 * <p><b>Пример:</b></p>
	 * <pre class="code">
	 *	var page1 = new widget.Tabs({
	 *		id : 'page1',
	 *		cls : 'page',
	 *		items : [
	 *			fieldName,
	 *			field1,
	 *			field2
	 *		],
	 *		renderTo : $('#fields'),
	 *		listeners : {
	 *			click : function(page, elementDom){
	 *				console.log('-- page1 Click!');
	 *			},
	 *			hide : function(page, elementDom){
	 *				console.log('-- page1 Cпрятался');
	 *			},
	 *			show : function(page, elementDom){
	 *				console.log('-- page1 Показался');
	 *			},
	 *			itemClick : function(index, item, elementDom){
	 *				console.log('-- Item '+index+' Click!');
	 *				console.log(item);
	 *				console.log(elementDom);
	 *			}
	 *		}
	 *	});
	 * </pre>
	 * 
	 * @constructs
	 * @param {Object} config Конфигурационный объект компонента.
	 * @param {String} config.id Идентификатор компонента.
	 * @param {String} config.cls Имя класса css (или нескольких классов через пробел)
	 * которые будут добавлены контейнеру.
	 * @param {Int} config.width Ширина компонента.
	 * @param {Int} config.height Высота компонента.
	 * @param {Object[]} config.items Массив компонент, которые должны быть включены в контейнер.
	 * @param {boolean} config.scrolling Признак того, что элементы внутри контейнера могут 
	 * скролироваться, если сумма высот всех входящих элементов больше высоты контейнера.
	 * @param {ElementDOM} config.renderTo Элемент DOM, в который будет прорисован контейнер.</br>
	 * @param {Object[]} config.listeners Массив слушателей событий
	 */
	constructor : function(config) {
		this.tabs = [];
		this.config = config || {} ;
		this.id = (config.id)? config.id : this.WIDGET_CLASS+this.generateId();
		this.config.id = this.id;
		if (this.config.tabs) {
			for (var i=0; i<this.config.tabs.length; i++) {
				this.addTab(new widget.Container({
					id : (this.getId()+'_'+i),
					label : this.config.tabs[i].label,
					items : this.config.tabs[i].items 
				}));
			}
		}
		
		if (this.config.renderTo) {
			try {
				this.render(this.config.renderTo);
			} catch (e) {
				var msg = 'widget.Tabs::render(): ';
				console.log("- Exception: ",msg);
				throw msg + e;
			}
		}
	},
	
	/**
	 * Эта функция возвращает идентификатор контейнера
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @return {string} - идентификатор контейнера
	 */
	getId : function() {
		return this.id;
	},
	
	/**
	 * Эта функция возвращает тип объекта
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @return {string} - тип объекта
	 */
	getType : function() {
		return this.type;
	},
	
	/**
	 * Возвращает признак видимости компонента закладок.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @return {boolean} - true, если объект видимый. Иначе false.
	 */
	isVisible : function() {
		return this.visible;
	},
	
	/**
	 * Установить признак видимости поля.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @param {boolean} visible признак доступности объекта.
	 * @return none
	 */
	setVisible : function(visible) {
		this.visible = visible;
	},
	
    /**
     * @private 
	 * @function
     * This function generate unique id using date-time, math round operation, reverse string
     * by length=10 chars
     * @return {string} id
    */
    generateId : function() {    
        return Math.round(((new Date()).getTime()+Math.random())*100).toString().split('').reverse().join('').substr(0,10);
    },

	/**
	 * Добавить компонент в массив закладок.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @param {Component} tab Объект компонента.
	 * @return {Component} tab Объект компонента.
	 */
	addTab : function(tab) {
		this.tabs.push(tab);
		return tab;
	},
	
	/**
	 * Возвращает массив закладок.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 */
	getTabs : function() {
		return this.tabs;
	},
	
	/**
	 * Очистить массив закладок.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @return none
	 */
	clearTabs : function() {
		this.tabs.length = 0;
	},
	
	/**
	 * Вставить новую закладку после указанной закладки.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 * @param {Component} tab Объект нового компонента.
	 * @param {string} baseTabId Идентификатор закладки, после которого вставить.
	 * @return none
	 */
	insertTabAfter : function(tab, baseTabId) {
		if (this.indexOfTabById(tab.getId) == -1) {
			var index = this.indexOfTabById(baseTabId);
			if (index > -1) {
				this.tabs.splice(index+1,0,tab);
			}
		}
	},
	
	/**
	 * Возвращает положение указанной закладки в массиве.
     * @private 
	 * @function
	 * @param {int} tabId Идентификатор закладки.
	 * @return {int} - Индекс закладки в массиве. Если закладка в массиве
	 * не найдена, будет возвращена -1.
	 */
	indexOfTabById : function(tabId) {
		for (var i = 0; i < this.tabs.length; i++) {
			if (this.tabs[i].id == tabId) {
				return i;
			}
		}
		return -1;
	},
	
	/**
	 * Удаляет закладку из массива.
     * @private 
	 * @function
	 * @param {string} tabId Идентификатор закладки.
	 * @return {int} - Индекс закладки в массиве. Если закладка в массиве
	 * не найдена, будет возвращена -1.
	 */
	removeTabById : function(tabId) {
		var index = this.indexOfTabById(tabId);
		if (index !== -1) {
			this.tabs.splice(index,1);
		}
		return index;
	},
	
	/**
	 * Выполняется отрисовка закладок.
     * @private 
	 * @function
	 * @param (elementDOM) renderTo Элемент DOM, внутри которого будет создан контейнер.
	 * @return none
	 */
	render : function(renderTo) {
		if (this.element) {
			this.element.remove();
		}

		var me = this;
		if (renderTo) {
			this.renderContainer = renderTo;
		} else {
			renderTo = this.renderContainer;
		}

		var tpl = '<div class="'+this.WIDGET_CLASS+'" id="' + this.getId() + '">';
		if (this.tabs) {
			tpl += '<ul>';
			for (var i=0; i<this.tabs.length; i++) {
				var tab = this.tabs[i];
				tpl += '<li><a href="#' + tab.getId() + '"><span>'+tab.getLabel()+'</span></a></li>';
			}
			tpl += '</ul>';
			for (var i=0; i<this.tabs.length; i++) {
				var tab = this.tabs[i];
				tpl += '<div id="' + tab.getId() + '"></div>';
			}
		}
		tpl += '</div>';
		
		this.element = $(tpl);
		
		if (this.config.width) {
			this.element.width(this.config.width);
		}
		if (this.config.height) {
			this.element.height(this.config.height);
		}
		
		if (this.config.cls) {
			this.applyClassToElement(this.config.cls, this.element)
		}		 
		if (renderTo) {
			$(this.element).appendTo(renderTo);
			$(this.element).click(function() {
				me.onClick();
			});
			for (var i=0; i<this.tabs.length; i++) {
				var tab = this.tabs[i];
				tab.render($('#' + tab.getId()));
			}
			this.element.tabs({select: function(event, ui) { me.onChange(ui.index);}});
			this.onAfterRender();
		}
	},
	
	/**
	 * Выполняется перерисовка одной или всех закладок.
     * @public 
     * @function 
     * @memberOf widget.Tabs#
     * @param {Integer} tabIndex Необязательный параметр, указывающий какую 
     * закладку надо перерисовать. Если не указан, перерисовываюься все закладки.
	 */
	refresh : function(tabIndex) {
		if (this.renderContainer) {
			if (tabIndex == undefined) {
				for (var i=0; i<this.tabs.length; i++) {
					this.tabs[i].refresh();
				}
			} else {
				this.tabs[tabIndex].refresh();
			}
		}
	},
	
	/**
	 * Функция отрабатывает сразу после прорисовки конпонента. 
     * @private 
	 * @function
	 */
	onAfterRender : function() {
		if (this.config.listeners && this.config.listeners.afterrender) {
			this.config.listeners.afterrender(this, this.element);
		}
	},
	
	/**
	 * Функция отрабатывает сразу после клика мышки на компоненте. 
     * @private 
	 * @function
	 */
	onClick : function() {
		if (this.config.listeners && this.config.listeners.click) {
			this.config.listeners.click(this, this.element);
		}
	},
	
	/**
	 * Спрятать закладки 
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 */
	hide : function() {
		var oldVisible = this.visible; 
		this.visible = false;
		if (this.visible != oldVisible) {
			this.element.addClass("hidden");
			this.onHide();
		}
	},
	
	/**
	 * Показать закладки 
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 */
	show : function() {
		var oldVisible = this.visible; 
		this.visible = true;
		if (this.visible != oldVisible) {
			this.element.removeClass("hidden");
			this.onShow();
		}
	},
	
	/**
	 * Удалить закладки 
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 */
	remove : function() {
		this.element.remove();
		this.onRemove();
	},
	
	/**
	 * Открыть вкладку, согласно указанному индексу.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 */
	selectTabByIndex : function(index) {
		this.element.tabs({selected : index});
		this.onChange(index);
	},
	
	/**
	 * Функция отрабатывает сразу после выбора вкладки. 
     * @private 
	 * @function
	 */
	onChange : function(index) {
		if (index != this.selectedTabIndex) {
			this.selectedTabIndex = index; 
			if (this.config.listeners && this.config.listeners.change) {
				this.config.listeners.change(this, index, this.tabs[index], this.element);
			}
		}
	},
	
	/**
	 * Функция отрабатывает после того, как элемент будет спрятан. 
     * @private 
	 * @function
	 */
	onHide : function() {
		if (this.config.listeners && this.config.listeners.hide) {
			this.config.listeners.hide(this, this.element);
		}
	},
	
	/**
	 * Функция отрабатывает после того, как элемент будет показан. 
     * @private 
	 * @function
	 */
	onShow : function() {
		if (this.config.listeners && this.config.listeners.show) {
			this.config.listeners.show(this, this.element);
		}
	},
	
	/**
	 * Функция отрабатывает после того, как элемент будет удален. 
     * @private 
	 * @function
	 */
	onRemove : function() {
		if (this.config.listeners && this.config.listeners.remove) {
			this.config.listeners.remove(this, this.element);
		}
	},
	
	/**
	 * Получить представление объекта в виде строки.
     * @public 
	 * @function
     * @memberOf widget.Tabs#
	 */
	toString : function() {
		return 'widget.control.Tabs:{"'+this.getType()+'":{"id":"'+this.getId()+'"}}'
	}

});
